<template>
  <div class="custom-form">
    <el-form ref="form" v-bind="$attrs" :model="data" :size="size">
      <template v-for="(item, index) in config">
        <div v-if="item.slotTitle" class="slot-title">
          {{ item.slotTitle }}
        </div>
        <el-form-item
          v-if="item.hide ? (item.hideValue.indexOf(data[item.hide]) === -1 ? false : true) : true"
          :key="item.prop"
          :label="isLabel ? item.label : ''"
          :prop="item.prop"
          :class="`item_${item.prop}`"
          :style="item.style"
          :rules="item.rules"
        >
          <template>
            <slot v-if="item.render ? item.render : false" v-bind="data" :name="item.prop" />

            <div v-else-if="isReadonly">
              {{ getReadyonlyText(data, item) }}
            </div>
            <!-- el-tree -->
            <el-tree
              v-else-if="item.type === 'tree'"
              v-model="data[item.prop]"
              node-key="id"
              :data="item.options"
              :props="item.props || { value: 'id', label: 'label', children: 'children' }"
              :show-checkbox="true"
              :check-strictly="false"
              :default-checked-keys="item.checkedKeys"
              @check-change="item.onCheckChange"
            />
            <!-- treeselect -->
            <treeselect
              v-else-if="item.type === 'treeselect'"
              v-model="data[item.prop]"
              :multiple="item.multiple"
              :options="item.options"
              :placeholder="item.placeholder || '请选择'"
              :normalizer="item.normalizer"
              :clearable="item.clearable || false"
              :disabled="item.disabled || false"
              :show-count="true"
            />
            <!-- 在 el-form-item 中添加上传组件 -->
            <el-form-item v-if="item.type === 'upload'" :key="item.prop" :prop="item.prop" :class="`item_${item.prop}`">
              <el-upload
                ref="uploadRef"
                :action="item.uploadUrl"
                :headers="item.headers"
                :multiple="item.multiple"
                :on-before-upload="onBeforeUpload"
                :on-success="onUploadSuccess"
                :disabled="item.disabled || false"
                :limit="item.limit"
                :show-file-list="true"
                :file-list="item.fileList"
                list-type="picture"
                :auto-upload="true"
              >
                <el-button slot="trigger" size="small" type="primary">上传图标</el-button>
                <!-- <el-button slot="tip" type="text" size="small">支持多文件上传</el-button> -->
              </el-upload>
            </el-form-item>

            <!-- input and textarea -->
            <el-input
              v-else-if="item.type === 'input' || item.type === 'textarea'"
              v-model.trim="data[item.prop]"
              :type="item.type === 'input' ? 'text' : 'textarea'"
              :rows="item.rows || 5"
              :disabled="item.disabled || false"
              :clearable="item.clearable || false"
              :placeholder="item.placeholder || '请填写内容'"
              :maxlength="item.maxlength"
              :show-word-limit="item.showWordLimit"
              :readonly="item.itemReadonly"
            >
              <template v-if="item.slotBefore" slot="prepend">{{ item.slotBefore }}</template>
            </el-input>

            <!-- input password -->
            <template v-else-if="item.type === 'input-password'">
              <el-input
                :ref="item.prop"
                v-model.trim="data[item.prop]"
                :type="passwordType[item.prop]"
                :rows="item.rows || 5"
                :disabled="item.disabled || false"
                :clearable="item.clearable || false"
                :placeholder="item.placeholder || '请填写内容'"
                :maxlength="item.maxlength"
                :show-word-limit="item.showWordLimit"
                :readonly="item.itemReadonly"
                autocomplete="new-password"
              />
              <span class="password-icon" @click="handlePassword(item.prop)">
                <svg-icon :icon-class="passwordType[item.prop] === 'password' ? 'eye' : 'eye-open'" />
              </span>
            </template>

            <!-- select -->
            <el-select
              v-else-if="item.type === 'select'"
              v-model="data[item.prop]"
              :multiple="item.multiple"
              :filterable="item.filterable"
              :allow-create="item.filterable"
              :disabled="item.itemReadonly || false"
              :clearable="item.clearable || false"
              :placeholder="item.placeholder || '请选择'"
              @change="$forceUpdate()"
            >
              <el-option
                v-for="_item in item.options"
                :key="_item.label"
                :label="_item.label"
                :value="_item.value"
                :disabled="_item.disabled"
              />
            </el-select>

            <!-- cascader -->
            <el-cascader
              v-else-if="item.type === 'cascader'"
              v-model="data[item.prop]"
              :options="item.options"
              :collapse-tags="item.collapse"
              :props="{
                ...item.props,
                multiple: item.multiple,
                checkStrictly: item.checkStrictly,
                emitPath: item.emitPath
              }"
              clearable
            />

            <!-- radio -->
            <el-radio-group v-else-if="item.type === 'radio'" v-model="data[item.prop]" v-removeAriaHidden>
              <el-radio
                v-for="radio in item.options"
                :key="radio.label"
                :label="radio.value"
                :disabled="item.itemReadonly"
                >{{ radio.label }}</el-radio
              >
            </el-radio-group>

            <!-- checkbox 返回数组-->
            <el-checkbox-group
              v-else-if="item.type === 'checkbox'"
              :value="checkboxVal(data[item.prop])"
              @input="($event) => $set(data, item.prop, $event)"
            >
              <el-checkbox v-for="checkbox in item.options" :key="checkbox.label" :label="checkbox.value">
                {{ checkbox.label }}
              </el-checkbox>
            </el-checkbox-group>

            <!-- date -->
            <el-date-picker
              v-else-if="item.type === 'date'"
              v-model="data[item.prop]"
              type="date"
              :clearable="false"
              :class="item.class || ''"
              :placeholder="item.placeholder"
              :format="item.format || 'yyyy-mm-dd'"
              :size="size"
            />

            <!-- dateTime -->
            <el-date-picker
              v-else-if="item.type == 'datetime'"
              v-model="data[item.prop]"
              type="datetime"
              :class="item.class || ''"
              :placeholder="item.placeholder"
              :format="item.format || 'yyyy-MM-dd HH:mm:ss'"
              :size="size"
            />
          </template>
        </el-form-item>
      </template>
    </el-form>
  </div>
</template>

<script>
import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
export default {
  name: 'CustomForm',
  components: {
    Treeselect
  },
  props: {
    onCheckChange: {
      type: Function,
      default: () => {}
    },
    onBeforeUpload: {
      type: Function,
      default: () => {}
    },
    onUploadSuccess: {
      type: Function,
      default: () => {}
    },

    value: {
      type: Object,
      default: () => {}
    },
    config: {
      type: Array,
      default: () => []
    },
    size: {
      type: String,
      default: 'medium'
    },
    isLabel: {
      type: Boolean,
      default: true
    },
    isReadonly: {
      type: Boolean,
      default: false
    }
  },
  data: function () {
    return {
      // 密码脱敏
      passwordType: {
        password: 'password'
      }
    }
  },
  computed: {
    data: {
      get: function () {
        return this.value
      },
      set: function (nval) {
        this.$emit('input', nval)
      }
    },
    checkboxVal: function () {
      return (v) => {
        return v == '' || v == null || v == undefined ? [] : v
      }
    }
  },
  watch: {
    config: {
      handler: function (nval) {
        nval.forEach(async (item) => {
          if (item.async && typeof item.async === 'function') {
            item.options = await item.async()
          }
        })
      },
      immediate: true
    }
  },
  methods: {
    handleTreeCheckChange(checkedKeys, node) {
      this.$emit('onCheckChange', checkedKeys, node) // 向外部发出事件
    },
    // handleBeforeUpload(file) {
    //   return this.$emit('onBeforeUpload', file)
    // },

    // handleUploadSuccess(res, file) {
    //   this.$emit('onUploadSuccess', res, file)
    // },
    validate() {
      return new Promise((reslove, reject) => {
        this.$refs['form'].validate((valid) => {
          valid ? reslove(true) : reject(false)
        })
      })
    },
    resetFields() {
      this.data = {}
      this.$refs['form'].resetFields()
    },
    clearValidate(prop) {
      this.$refs['form'].clearValidate(prop)
    },
    validateField(prop) {
      this.$refs['form'].validateField(prop)
    },
    clear(obj) {
      const result = {}
      for (const k in obj) {
        const val = obj[k]
        if (val !== '' && val !== undefined && val !== null) result[k] = val
      }
      return result
    },
    getReadyonlyText(data, item) {
      const value = data[item.prop]
      if (item.options) {
        return this.dictLabel(item.options, value) ?? '-'
      }
      return value ?? '-'
    },
    dictLabel(options, value, KEY = 'label') {
      if (!options instanceof Array || !options.length) return ''
      try {
        return options.find((i) => i['value'] === value)[KEY]
      } catch (error) {}
    },
    cascaderChange(valuesArr, item) {
      this.$nextTick(() => {
        var newArr = []
        valuesArr.map((item) => {
          newArr.push(item[item.length - 1])
        })
        this.data[item.prop] = newArr
      })
    },
    handlePassword(str) {
      this.passwordType[str] = this.passwordType[str] === 'password' ? '' : 'password'
      this.$nextTick(() => this.$refs[str][0].focus())
    }
  }
}
</script>

<style lang="scss" scoped>
.slot-title {
  width: 100%;
  font-weight: bold;
  font-size: 14px;
  color: #333333;
  padding: 10px 0 30px 0;
  font-style: normal;
}
.el-form {
  display: flex;
  justify-content: flex-start;
  align-items: flex-start;
  flex-wrap: wrap;
  .el-form-item {
    width: 100%;
    padding: 0 20px;
    box-sizing: border-box;
    display: flex;
    justify-content: flex-start;
    align-items: flex-start;
    flex-wrap: wrap;
  }
  &.el-form--inline > .el-form-item {
    width: auto;
  }
  ::v-deep .el-form-item__content {
    margin-left: 0 !important;
    flex: 1;
  }
  ::v-deep .is-required .el-form-item__label {
    position: relative;
    &:before {
      display: none;
      content: '';
    }
    &:after {
      content: '*';
      color: #f56c6c;
      position: absolute;
      right: 0;
      top: 0;
    }
  }
  ::v-deep .is-no-asterisk .el-form-item__label {
    &:after {
      display: none !important;
    }
  }
  ::v-deep.el-form-item__label {
    margin-right: 10px;
    font-size: 14px;
    color: #333333;
    font-weight: initial;
  }
}
::v-deep .el-form--label-top {
  .el-form-item__label,
  .el-form-item__content {
    width: 100%;
  }
  .el-form-item__label {
    line-height: 20px;
  }
  .is-required .el-form-item__label {
    &:after {
      content: '*';
      color: #f56c6c;
      position: initial !important;
      margin-left: 5px;
    }
  }
  .is-no-asterisk .el-form-item__label {
    &:after {
      display: none !important;
    }
  }
}
.el-input,
.el-select,
.el-cascader {
  width: 100%;
}
.password-icon {
  position: absolute;
  right: 10px;
  top: 50%;
  margin-top: -18px;
  font-size: 12px;
  color: #c0c4cc;
  cursor: pointer;
  user-select: none;
}
</style>
